package com.jetpack.jetpacknavtest.base

import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.annotation.IdRes
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator
import java.util.*

//实现跳转返回不重建

@Navigator.Name("fragment")
class MyFragmentNavigator(val mContext: Context, val mFragmentManager: FragmentManager, val mContainerId: Int)
    : FragmentNavigator(mContext, mFragmentManager, mContainerId) {
    var mIsPendingBackStackOperation = false
    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?): NavDestination? {

        val TAG = "MyFragmentNavigator"
        if (mFragmentManager.isStateSaved) {
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already" + " saved its state")
            return null
        }
        var className = destination.className
        if (className[0] == '.') {
            className = mContext.packageName + className
        }
//        val frag = instantiateFragment(mContext, mFragmentManager,
//                className, args)
//        frag.arguments = args
        val ft = mFragmentManager.beginTransaction()

        var enterAnim = navOptions?.enterAnim ?: -1
        var exitAnim = navOptions?.exitAnim ?: -1
        var popEnterAnim = navOptions?.popEnterAnim ?: -1
        var popExitAnim = navOptions?.popExitAnim ?: -1
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = if (enterAnim != -1) enterAnim else 0
            exitAnim = if (exitAnim != -1) exitAnim else 0
            popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
            popExitAnim = if (popExitAnim != -1) popExitAnim else 0
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
        }

        // 反射获取返回栈
        var mBackStack: ArrayDeque<Int>? = null
        try {
            val declaredField = FragmentNavigator::class.java.getDeclaredField("mBackStack")
            declaredField.isAccessible = true
            mBackStack = declaredField.get(this) as ArrayDeque<Int>
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: SecurityException) {

        } catch (e: IllegalArgumentException) {

        } catch (e: IllegalAccessException) {

        }

        //需要hide 返回栈的 fragment 但是返回时候也要处理
        for (index in mBackStack!!) {
            var fragTemp = mFragmentManager.findFragmentByTag(index.toString())
            ft.hide(fragTemp!!)
        }


        var tag = destination.id.toString()
        var frag = mFragmentManager.findFragmentByTag(tag) //导航图ID

        if (frag == null) {
            frag = instantiateFragment(mContext, mFragmentManager, className, args)
            frag.arguments = args
            ft.add(mContainerId, frag, tag)

        } else {
            ft.show(frag)
        }
//        ft.replace(mContainerId, frag)
        ft.setPrimaryNavigationFragment(frag)


        @IdRes val destId = destination.id
        val initialNavigation = mBackStack?.isEmpty()
        // TODO Build first class singleTop behavior for fragments
        // TODO Build first class singleTop behavior for fragments
        val isSingleTopReplacement = (navOptions != null && !initialNavigation!!
                && navOptions.shouldLaunchSingleTop()
                && mBackStack?.peekLast() == destId)

        val isAdded: Boolean
        if (initialNavigation!!) {
            isAdded = true
        } else if (isSingleTopReplacement) {
            // Single Top means we only want one instance on the back stack
            if (mBackStack?.size!! > 1) {
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStack(
                        generateBackStackName(mBackStack.size, mBackStack?.peekLast()),
                        FragmentManager.POP_BACK_STACK_INCLUSIVE)
                ft.addToBackStack(generateBackStackName(mBackStack?.size, destId))
                mIsPendingBackStackOperation = true
            }
            isAdded = false
        } else {
            ft.addToBackStack(generateBackStackName(mBackStack?.size!! + 1, destId))
            mIsPendingBackStackOperation = true
            isAdded = true
        }
        if (navigatorExtras is Extras) {
            for ((key, value) in navigatorExtras.sharedElements) {
                ft.addSharedElement(key!!, value!!)
            }
        }
        ft.setReorderingAllowed(true)
        ft.commit()
        // The commit succeeded, update our view of the world
        // The commit succeeded, update our view of the world
        return if (isAdded) {
            mBackStack?.add(destId)
            destination
        } else {
            null
        }
    }

    private fun generateBackStackName(backStackIndex: Int, destId: Int): String {
        return "$backStackIndex-$destId"
    }
}